import pygame
import os
from .constant import SNAKE_SKINS


# 蛇类
# 点以25为单位
# object 代表这是一个标准的类
# 这是“构造函数”或“初始化方法”，在创建 Snake 对象时会自动调用
# 比如 `player_snake = Snake(...)` 就会执行这里的代码
class Snake(object):
    # --- 修改: 添加了start_pos和start_dir用于自定义生成位置 ---
    # self 代表被创建的那个对象自身
    def __init__(self, base_dir, skin_id='default', start_pos=None, start_dir=None,initial_length=4):
        self.dirction = start_dir if start_dir is not None else pygame.K_RIGHT
        self.body = [] # 蛇的身体，是一个列表，里面存放了很多个矩形(Rect)
        self.base_dir = base_dir
        self.head_image = self._load_head_image()
        self.base_speed = 6
        self.speed_multiplier = 1.2  # 速度倍率（用于特殊食物加速）
        self.skin_colors = SNAKE_SKINS.get(skin_id, SNAKE_SKINS['default'])['colors']
        """.get()    1.字典的 .get() 方法
        2..get() 方法则更“安全”，它有两个参数：
        a_dictionary.get(key, default_value)
        第一个参数 key: 你想要查找的键。
        第二个参数 default_value: 如果找不到这个 key，就返回这个默认值，而不会报错。
        3.尝试根据传入的 skin_id 去 SNAKE_SKINS 配置中查找对应的皮肤信息；
        如果找不到，就使用名为 'default' 的皮肤信息作为替代。无论找到的是哪个，
        都从该皮肤信息中提取出 'colors' 颜色列表，并将其存入当前对象的 skin_colors 属性中。"""
        # --- 修改: 根据start_pos初始化身体 ---
        initial_pos = start_pos if start_pos is not None else (100, 100)
        for i in range(initial_length):
            node = pygame.Rect(initial_pos[0], initial_pos[1], 25, 25)
            #pygame.Rect(left, top, width, height),
            #在创建一个新的 Rect 对象。括号里的四个参数，是在告诉 Pygame 这个“虚拟方块”的属性
            # 在头部后面创建初始身体段
            if self.dirction == pygame.K_LEFT:
                node.left += i * 25
            elif self.dirction == pygame.K_RIGHT:#蛇头朝右，身体在左边
                node.left -= i * 25
            elif self.dirction == pygame.K_UP:
                node.top += i * 25
            elif self.dirction == pygame.K_DOWN:
                node.top -= i * 25
            self.body.insert(0, node)
            #insert(0, ...)，每次都把新生成的节点插入到列表的最前面

    # 新增：获取当前实际速度（基础速度×倍率）
    def get_current_speed(self):
        return int(self.base_speed * self.speed_multiplier)

    def _load_head_image(self):
        """前面的下划线 _ 通常是一种约定，表示这个方法主要是供类的内部使用的，不是给外部直接调用的"""
        """加载并缩放蛇头图片"""
        head_path = os.path.join(self.base_dir, "head.png")
        #os.path.join() 是一个非常重要且常用的函数。它的作用是智能地将多个路径部分拼接成一个完整的路径。
        try:
            img = pygame.image.load(head_path)
            """
            工作流程是：
            它去你的硬盘上找到那个 .png 或 .jpg 文件（比如 head.png）。
            它读取这个文件。但这个文件本身是一种“压缩格式”，电脑显卡是不能直接把它画到屏幕上的。
            pygame.image.load() 会**“解压缩”这个文件，把它转换成显卡能直接理解的、原始的像素数据**。
            在 Pygame 中，用来承载这种“原始像素数据”的标准容器，就叫做 Surface 对象。
            Pygame 中，你只能对 Surface 对象进行操作。
            
            1.pygame.image.load() 是 Pygame 专门用来从硬盘加载图片文件的函数。
            2.根据我们刚刚拼接好的 head_path 路径，找到 head.png 文件，
            并把它加载到内存中，存为 img 变量。img 现在就是一个包含图片数据的 Surface 对象。"""
            return pygame.transform.scale(img, (25, 25))  # 缩放到与蛇身一致
            """
            1.pygame.transform.scale() 是一个用来调整（缩放）图片尺寸的函数。
            2.它接收两个参数：第一个是要缩放的图片 (img)，第二个是目标尺寸的元组 (宽度, 高度)。
            3.它把加载进来的蛇头图片，不论原始尺寸多大，都统一缩放成 25x25 像素的正方形，以确保蛇头和身体的每一节大小都一样。
            4.return 关键字会把这个缩放后的图片对象作为函数的执行结果返回给调用它的地方。
            """
        except Exception as e:
            # 如果加载失败，打印错误信息
            #将捕获到的这个异常（Exception）对象，赋值给一个名为 e 的变量
            print(f"❌ 蛇头图片缺失！请将 head.png 放在 {self.base_dir} 目录")
            print(f"   错误详情：{str(e)}")
            return None

    # 无论何时 都在前端增加蛇块
    # 在蛇头前进的方向增加一个新节点
    def addnode(self):
        # 默认新节点位置与蛇头重合
        left, top = (0, 0)
        if self.body:# 确保蛇身体不为空
            left, top = (self.body[0].left, self.body[0].top)
        node = pygame.Rect(left, top, 25, 25)
        # 根据当前方向，调整新节点的位置，使其出现在蛇头前方
        if self.dirction == pygame.K_LEFT:
            node.left -= 25
        elif self.dirction == pygame.K_RIGHT:
            node.left += 25
        elif self.dirction == pygame.K_UP:
            node.top -= 25
        elif self.dirction == pygame.K_DOWN:
            node.top += 25
        # 将这个新节点插入到身体列表的最前面，成为新的蛇头
        self.body.insert(0, node)

    # 删除最后一个块
    # 删除蛇尾的最后一个节点
    def delnode(self):
        if len(self.body) > 1:  # 防止删除蛇头
            # pop()默认删除列表最后一个元素
            self.body.pop()

    # 死亡判断
    def isdead(self, screen_x, screen_y, obstacles):
        # 撞墙
        # 判断是否撞墙
        if self.body[0].x not in range(screen_x):
            return True

        if self.body[0].y not in range(screen_y):
            return True
        # 撞自己
        # if self.body[0] in self.body[1:]:
        #    return True
        # 判断是否撞到障碍物
        for obs in obstacles:
            if self.body[0].colliderect(obs["rect"]):
                return True
        # 如果以上情况都未发生，则返回 False (未死亡)
        return False

    # 移动！
    # 移动方法 (核心逻辑)
    def move(self):
        self.addnode()# 在蛇头前面增加一节
        self.delnode()# 同时在蛇尾删除一节
        # 整体效果就是整条蛇向前移动了一格

    # 改变方向 但是左右、上下不能被逆向改变
    def changedirection(self, curkey):
        #curkey：current key 的缩写，代表玩家当前按下的那个键是什么（比如 pygame.K_UP）。
        LR = [pygame.K_LEFT, pygame.K_RIGHT]
        UD = [pygame.K_UP, pygame.K_DOWN]
        # 检查按键是否是方向键
        if curkey in LR + UD:
            # 如果蛇正在水平移动，则忽略左右键
            if (curkey in LR) and (self.dirction in LR):
                return
            # 如果蛇正在垂直移动，则忽略上下键
            if (curkey in UD) and (self.dirction in UD):
                return
            # 如果不是反方向，则更新方向
            """
            1.说明它通过了前面所有的 if 检查，它既不是无效按键，也不是掉头操作
            2.这是一个 90 度的有效转弯
            """
            self.dirction = curkey


# ========== 添加圆形蛇身绘制函数 ==========
def draw_snake_body(screen, snake):
    # 定義紫色
    COLOR_PURPLE = (138, 43, 226)
    """绘制圆形蛇身，偶数段为原绿色，奇数段为深红色"""
    # enumerate 函数
    """enumerate(snake.body) 会同时返回两个值：
    1.i：当前蛇段的索引（从 0 开始，0 表示蛇头，1 表示第一节身体，以此类推）
    2.rect：当前蛇段的 Rect 对象，包含该段的位置（x, y）和大小信息
    enumerate 在遍历蛇身体各段时，自动记录了当前是第几段（索引），避免了手动维护计数器的麻烦。"""
    for i, rect in enumerate(snake.body):
        """
        1.snake.body 是一个列表，里面装了蛇的所有身体方块（Rect 对象）。
        2.用 for i, rect in enumerate(snake.body): 时，enumerate 会自动帮你：
        从 snake.body 列表里拿出一个元素（比如第一个身体方块），把它放进 rect 变量。
        同时，它会给这个元素一个从 0 开始的编号（索引），并把这个编号放进 i 变量。
        在循环中：
    i 就是索引（编号），依次会是 0, 1, 2, 3, ...
    rect 就是列表中的元素（身体方块），依次是 body[0], body[1], body[2], ...
        """
        # 计算圆形位置（使圆形中心与矩形中心对齐）
        center_x = rect.x + 12
        center_y = rect.y + 12
        radius = 12  # 半径比原来25x25的矩形稍小

        # --- 核心修改 ---: 根据索引奇偶和皮肤设置来选择颜色
        color = snake.skin_colors[0] if i % 2 == 0 else snake.skin_colors[1]

        """
        1.i % 2 == 0 是在判断索引 i 是偶数还是奇数。
        如果i 是偶数（比如 0, 2, 4...），就使用第 1 个颜色 snake.skin_colors[0]。
        如果 i 是奇数（比如 1, 3, 5...），就使用第 2 个颜色 snake.skin_colors[1]。
        意思：我要用两种颜色来画蛇，让蛇的身体呈现一节一节的‘条纹’效果。偶数节身体用颜色1，奇数节身体用颜色2
        """

        """
        1.if i == 0 ... 这行代码的意思就是： 
        “检查一下，我们当前正在处理的这个身体方块，是不是列表里的第 0 个？如果是，那它就是蛇头
        """
        if i == 0 and hasattr(snake, 'is_ai') and snake.is_ai:
            color = COLOR_PURPLE
        """
        1.hasattr() 是 Python 的一个内置函数，它的名字是 "has attribute"（拥有属性）的缩写
        功能：用来安全地检查一个“对象”是否“拥有”某个“属性”（变量）。
        语法：hasattr(object, 'attribute_name')
        返回值：如果对象有这个属性，它返回 True；如果没有，它返回 False。
        
        """

        # 绘制圆形蛇身
        # 绘制身体节点的圆形
        pygame.draw.circle(screen, color, (center_x, center_y), radius)

        # --- 新功能: 在AI蛇的头部绘制眼睛 ---
        if i == 0 and hasattr(snake, 'is_ai') and snake.is_ai:
            eye_radius = 2
            eye_offset = 5
            eye_color = (0, 0, 0)  # 黑色的眼睛

            if snake.dirction == pygame.K_RIGHT:
                eye1_pos = (center_x + eye_offset, center_y - eye_offset)
                eye2_pos = (center_x + eye_offset, center_y + eye_offset)
            elif snake.dirction == pygame.K_LEFT:
                eye1_pos = (center_x - eye_offset, center_y - eye_offset)
                eye2_pos = (center_x - eye_offset, center_y + eye_offset)
            elif snake.dirction == pygame.K_UP:
                eye1_pos = (center_x - eye_offset, center_y - eye_offset)
                eye2_pos = (center_x + eye_offset, center_y - eye_offset)
            else:  # K_DOWN
                eye1_pos = (center_x - eye_offset, center_y + eye_offset)
                eye2_pos = (center_x + eye_offset, center_y + eye_offset)

            pygame.draw.circle(screen, eye_color, eye1_pos, eye_radius)
            """
            circle 是 Pygame 库自带的一个核心函数
            1.它完整的位置在 pygame.draw 模块里，draw 就是“绘制”的意思
            2.功能非常直接：在屏幕上画一个实心的圆圈。
            3.pygame.draw.circle(画在哪里, 用什么颜色, 画在哪个位置, 画多大)
            """
            pygame.draw.circle(screen, eye_color, eye2_pos, eye_radius)

        # 同样修改绘制蛇头图片的判断逻辑
        if i == 0 and snake.head_image and not (hasattr(snake, 'is_ai') and snake.is_ai):
            if snake.dirction == pygame.K_RIGHT:
                rotated_head = snake.head_image
            elif snake.dirction == pygame.K_LEFT:
                """
                1.transform 是 pygame 库里的一个模块，你可以把它想象成一个“图像变换工具箱”
                pygame.transform。
                这个“工具箱”里装满了各种用来改变图片的工具，比如：
                缩放图片 (scale)   旋转图片 (rotate)    翻转图片 (flip)
                2.功能：它的作用是把一张图片（一个 Surface 对象）旋转指定的角度，然后返回一张旋转后的新图片。
                语法：pygame.transform.rotate(要旋转的图片, 旋转的角度)
                角度：在 Pygame 中，角度是逆时针计算的。0 度代表原始方向（通常是朝右）。
                """
                rotated_head = pygame.transform.rotate(snake.head_image, 180)
            elif snake.dirction == pygame.K_UP:
                rotated_head = pygame.transform.rotate(snake.head_image, 90)
            else:
                rotated_head = pygame.transform.rotate(snake.head_image, -90)

            # 图片与蛇头圆形对齐
            head_img_rect = rotated_head.get_rect(center=(center_x, center_y))
            """
            get_rect(center=(center_x, center_y)) 部分：
            这是 .get_rect() 的一个超级快捷方式。
            它在创建那个矩形 Rect 的同时，还会立刻把这个矩形的中心点移动到你指定的 center=(center_x, center_y) 这个坐标上。
            center_x, center_y 是你之前计算好的蛇头那个圆形的中心点。

            """
            screen.blit(rotated_head, head_img_rect)
            """
            screen.blit(rotated_head, head_img_rect) 
            把旋转好的图片 rotated_head 画在刚刚计算好的矩形 head_img_rect 的位置上。blit 会自动使用矩形的左上角坐标来定位
            """
